//
//  Framework for a raytracer
//  File: triangle.cpp
//
//  Created for the Computer Science course "Introduction Computer Graphics"
//  taught at the University of Groningen by Tobias Isenberg.
//
//  Authors:
//	  Zhe Sun
//
//  This framework is inspired by and uses code of the raytracer framework of 
//  Bert Freudenberg that can be found at
//  http://isgwww.cs.uni-magdeburg.de/graphik/lehre/cg2/projekt/rtprojekt.html 
//

#include "triangle.h"
#include <iostream>
#include <math.h>

/************************** Triangle **********************************/
#if 0
Hit Triangle::intersect(const Ray &ray)
{
     // All code below is based on page79 of the book
  double a,b,c,d,e,f,g,h,i,j,k,l;
  a = v1.x - v2.x;    d = v1.x - v3.x;   g = ray.D.x;   j = v1.x - ray.O.x;
  b = v1.y - v2.y;    e = v1.y - v3.y;   h = ray.D.y;   k = v1.y - ray.O.y;  
  c = v1.z - v2.z;    f = v1.z - v3.z;   i = ray.D.z;   l = v1.z - ray.O.z;

  double ei_hf = e*i - h*f;
  double gf_di = g*f - d*i;
  double dh_eg = d*h - e*g;
  double ak_jb = a*k - j*b;
  double jc_al = j*c - a*l;
  double bl_kc = b*l - k*c;

  double M = a*ei_hf + b*gf_di + c*dh_eg;

  // computer gamma and judge if hit
  double gamma = (i*ak_jb + h*jc_al + g*bl_kc)/M;
  if (gamma < 0 || gamma > 1)
    return Hit::NO_HIT();

  // computer beta and judge if hit
  double beta = (j*ei_hf + k*gf_di + c*dh_eg)/M;
  if (beta < 0 || beta > (1 - gamma))
    return Hit::NO_HIT();

  // computer the distance
  double t = -(f*ak_jb + e*jc_al + d*bl_kc)/M;
  if ( t < 0 )
    return Hit::NO_HIT();

  // now, the ray can hit the triangle

  // cross multiply get the normal
  Vector a_b = v2 - v1;
  Vector a_c = v3 - v1;

  Vector N = a_b.cross(a_c);   
  return Hit(t, N.normalized());
}
#else
Hit Triangle::intersect(const Ray &ray)
{
    double u, v;
    Vector edge1, edge2, tvec, pvec, qvec;
    double det, invDet;
    
    edge1 = v2 - v1;
    edge2 = v3 - v1;
    pvec = ray.D.cross(edge2);
    
    det = edge1.dot(pvec);

   if(det > -.000001 && det < .000001)
   {
      return Hit::NO_HIT();
   }
   invDet = 1/det;

   tvec = ray.O - v1;

   u = tvec.dot(pvec) * invDet;
   if(u < 0 || u > 1)
   {
        return Hit::NO_HIT();
   }

   qvec = tvec.cross(edge1);

   v = ray.D.dot(qvec) * invDet;
   if(v < 0 || (u + v) > 1)
   {
        return Hit::NO_HIT();
   }
      
    
    double t = edge2.dot(qvec) * invDet;
    if(t < 0)
    {
        return Hit::NO_HIT();
   }
   
    return Hit(t, pvec.normalized());;
}
#endif